<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="android mio4kon mio">
    <meta name="keyword"  content="">
    <link rel="shortcut icon" href="/img/favicon.ico">

    <title>
        
          自动化之旅--Appium - Mio4kon的博客 | Mio4kon&#39;s Blog
        
    </title>

    <link rel="canonical" href="http://mio4kon.com/2017/02/13/自动化之旅--Appium/">

    <!-- Bootstrap Core CSS -->
    <link rel="stylesheet" href="/css/bootstrap.min.css">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/hux-blog.min.css">

    <!-- Pygments Highlight CSS -->
    <link rel="stylesheet" href="/css/highlight.css">

    <!-- Custom Fonts -->
    <!-- <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css"> -->
    <!-- Hux change font-awesome CDN to qiniu -->
    <link href="https://cdn.staticfile.org/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">


    <!-- Hux Delete, sad but pending in China
    <link href='http://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link href='http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/
    css'>
    -->


    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

    <!-- ga & ba script hoook -->
    <script></script>
</head>


<!-- hack iOS CSS :active style -->
<body ontouchstart="">

    <!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">Mio4kon</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <!-- Known Issue, found by Hux:
            <nav>'s height woule be hold on by its content.
            so, when navbar scale out, the <nav> will cover tags.
            also mask any touch event of tags, unfortunately.
        -->
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>

                    

                        
                    

                        
                        <li>
                            <a href="/about/">About</a>
                        </li>
                        
                    

                        
                        <li>
                            <a href="/archives/">Archives</a>
                        </li>
                        
                    

                        
                        <li>
                            <a href="/tags/">Tags</a>
                        </li>
                        
                    
                    
                </ul>
            </div>
        </div>
        <!-- /.navbar-collapse -->
    </div>
    <!-- /.container -->
</nav>
<script>
    // Drop Bootstarp low-performance Navbar
    // Use customize navbar with high-quality material design animation
    // in high-perf jank-free CSS3 implementation
    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        // CLOSE
            $navbar.className = " ";
            // wait until animation end.
            setTimeout(function(){
                // prevent frequently toggle
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        // OPEN
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>


    <!-- Main Content -->
    
<!-- Image to hack wechat -->
<!-- <img src="http://mio4kon.com/img/icon_wechat.png" width="0" height="0"> -->
<!-- <img src="{{ site.baseurl }}/{% if page.header-img %}{{ page.header-img }}{% else %}{{ site.header-img }}{% endif %}" width="0" height="0"> -->

<!-- Post Header -->
<style type="text/css">
    header.intro-header{
        background-image: url('post-bg.jpg')
    }
</style>
<header class="intro-header" >
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="post-heading">
                    <div class="tags">
                        
                          <a class="tag" href="/tags/#Appium" title="Appium">Appium</a>
                        
                    </div>
                    <h1>自动化之旅--Appium</h1>
                    <h2 class="subheading">走进自动化</h2>
                    <span class="meta">
                        Posted by Mio4kon on
                        2017-02-13
                    </span>
                </div>
            </div>
        </div>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">

    <!-- Post Container -->
            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                post-container">

                <p></p><p style="text-align:right"> <img src="media/15029506211642.jpg" alt=""></p>
<p>为了避免每次上线前重复的人工回归测试,保证每次上线的版本不会引起核心业务的不稳定,所以急需自动化测试来保证业务的稳定性.经过调研我尝试使用Appium进行自动化测试,原因是功能强大,跨平台而且社区也很活跃.</p>
<h1 id="主流框架对比"><a href="#主流框架对比" class="headerlink" title="主流框架对比"></a>主流框架对比</h1><p><img src="http://mio4kon.qiniudn.com/14869609987714.jpg?imageView2/1/w/900/h/440|imageMogr2" alt="-w682"></p>
<h1 id="Appium优点"><a href="#Appium优点" class="headerlink" title="Appium优点"></a>Appium优点</h1><ul>
<li>开源</li>
<li>跨架构:Native App、Hybird App、Web App</li>
<li>跨设备:Android、iOS、Firefox OS</li>
<li>不依赖源码</li>
<li>使用任何 WebDriver 兼容的语言来编写测试用例。比如 Java， Objective-C， JavaScript with Node.js (in both callback and yield-based flavours)， PHP， Python， Ruby， C#， Clojure， 或者 Perl.</li>
<li>不需要重新编译APP</li>
</ul>
<p>如果有不清楚WebDriver的小伙伴马上在Appium架构介绍中会有说明.</p>
<h1 id="Appium理念"><a href="#Appium理念" class="headerlink" title="Appium理念"></a>Appium理念</h1><ol>
<li>你无需为了自动化，而重新编译或者修改你的应用。</li>
<li>你不必局限于某种语言或者框架来写和运行测试脚本。</li>
<li>一个移动自动化的框架不应该在接口上重复造轮子。（移动自动化的接口应该统一）</li>
<li>无论是精神上，还是名义上，都必须开源。</li>
</ol>
<h1 id="Appium架构"><a href="#Appium架构" class="headerlink" title="Appium架构"></a>Appium架构</h1><p><img src="http://mio4kon.qiniudn.com/14869635664446.jpg?imageView2/1/w/900/h/440|imageMogr2" alt="-w664"></p>
<p><strong>iOS:</strong> 苹果的UIAutomation<br><strong>Android 4.2+:</strong> Google的UiAutomator<br><strong>Android 2.3+:</strong> Google’s Instrumentation. (由单独的项目Selendroid提供支持 )   </p>
<p><strong>Appium 1.6版本以上增加了<code>UiAutomator2</code></strong></p>
<p>为了满足上面跨平台,把这些三方框架封装成一套API —— WebDriver Api(客户端到服务端的协议)</p>
<p>事实上 WebDriver 已经成为 web 浏览器自动化的标准，也成了 W3C 的标准 —— <a href="https://w3c.github.io/webdriver/webdriver-spec.html" target="_blank" rel="external">W3C Working Draft</a>,所以Appium在原有基础上扩充了移动自动化相关的API.</p>
<p>投资 WebDriver 意味着你可以押宝在一个已经成为标准的独立，自由和开放的协议。你不会被任何专利限制。</p>
<p><strong>核心架构</strong>: Appium使用C/S架构,运行时候Service端会监听Client端发送的命令,接着在移动设备上执行这些命令，然后将执行结果放在 HTTP 响应中返还给客户端.</p>
<p>基于这架构可以做什么? </p>
<ul>
<li>可以用任何实现了该客户端的语言来写测试代码</li>
<li>可以把服务端放在不同的机器上</li>
<li>可以只写测试代码,然后利用类似 <a href="https://saucelabs.com/" target="_blank" rel="external">saucelabs</a> 云服务来解释命令.</li>
</ul>
<p>下图解释了云服务的具体作用:</p>
<p><img src="http://mio4kon.qiniudn.com/14872240688085.jpg" alt=""></p>
<h1 id="Appium-使用"><a href="#Appium-使用" class="headerlink" title="Appium 使用"></a>Appium 使用</h1><h2 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h2><ul>
<li><p>安装Appium服务器 </p>
<pre><code>npm install -g appium
npm install -g appium-doctor
appium-doctor
</code></pre></li>
</ul>
<p>其中<code>appium-doctor</code>用来检查电脑是否缺少相关依赖.当所有都是对勾表示Appium环境配置完毕,如下:</p>
<p><img src="http://mio4kon.qiniudn.com/14872367178116.jpg?imageView2/1/w/900/h/440|imageMogr2" alt=""></p>
<ul>
<li><p>开启appium服务器:</p>
<pre><code>appium --address 127.0.0.1 --port 4723 --log &quot;/Users/mio4kon/Desktop/
appium.log&quot; --log-timestamp --local-timezone --session-override
</code></pre></li>
</ul>
<h2 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h2><p>再次强调 <code>Appium</code> 支持各种语言,这里我选择JAVA.如果觉得JAVA语法不够简洁或者不熟悉,可以使用你所熟悉的语言.</p>
<h3 id="创建-MAVEN-Gradle-工程"><a href="#创建-MAVEN-Gradle-工程" class="headerlink" title="创建 MAVEN/Gradle 工程:"></a>创建 MAVEN/Gradle 工程:</h3><p>创建工程,并加入下面依赖:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>io.appium<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>java-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>5.0.0-BETA2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.seleniumhq.selenium<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>selenium-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">        <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">exclusions</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.seleniumhq.selenium<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>selenium-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.0.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div></pre></td></tr></table></figure>
<p>这样Appium 客户端的依赖就引入成功了.</p>
<h3 id="Capabiltiy配置"><a href="#Capabiltiy配置" class="headerlink" title="Capabiltiy配置:"></a>Capabiltiy配置:</h3><p>每个Test的基类中定义<code>setUp</code>方法,并设置<code>Capabiltiy</code>以前其他的初始化操作:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">DesiredCapabilities capabilities = <span class="keyword">new</span> DesiredCapabilities ();</div><div class="line">capabilities.setCapability (MobileCapabilityType.DEVICE_NAME, deviceName);</div><div class="line">capabilities.setCapability (MobileCapabilityType.PLATFORM_NAME, platformName);</div><div class="line">capabilities.setCapability (MobileCapabilityType.PLATFORM_VERSION, platformVersion);</div><div class="line">capabilities.setCapability (MobileCapabilityType.APP, apkPath);</div><div class="line">capabilities.setCapability (AndroidMobileCapabilityType.APP_PACKAGE, appPackage);</div><div class="line">capabilities.setCapability (AndroidMobileCapabilityType.APP_ACTIVITY, appActivity);</div><div class="line">capabilities.setCapability (MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);</div></pre></td></tr></table></figure>
<p>这里我使用的是 <a href="http://testng.org/doc/index.html" target="_blank" rel="external">TestNG</a> 中的<code>Parameters</code>注释来配置参数.</p>
<p><img src="http://mio4kon.qiniudn.com/14872464979722.jpg" alt=""></p>
<p>TestNG 又是什么鬼? 简单来说TestNG是java的一个测试框架,类似JUnit但功能更加强大,使用也方便.</p>
<h3 id="TestNG"><a href="#TestNG" class="headerlink" title="TestNG"></a>TestNG</h3><p>利用TestNg的一些注释,做准备化和收尾操作.</p>
<p><img src="http://mio4kon.qiniudn.com/14872471690847.jpg" alt=""></p>
<p>上面的<code>setUp</code>方法我就使用了<code>BeforeClass</code>和<code>Parameters</code>这两种注释.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@BeforeClass</span></div><div class="line"><span class="meta">@Parameters</span>(&#123;<span class="string">"driverName"</span>, <span class="string">"url"</span>, <span class="string">"deviceName"</span>, <span class="string">"platformName"</span>, <span class="string">"platformVersion"</span>, <span class="string">"apkPath"</span>, <span class="string">"appPackage"</span>, <span class="string">"appActivity"</span>&#125;)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setUp</span><span class="params">(String driverName, String url, String deviceName,</span></span></div><div class="line">                  String platformName, String platformVersion, String apkPath,</div><div class="line">                  String appPackage, String appActivity) <span class="keyword">throws</span> Exception &#123;</div><div class="line">    log.i (TAG, <span class="string">"BeforeClass"</span>);</div><div class="line">    driver = setRemoteDriver (driverName, url, deviceName, platformName, platformVersion, apkPath, appPackage, appActivity);</div><div class="line">    actions = ElementActions.getInstance ().init (driver);</div><div class="line">    assertActions = actions.getAssertActions ();</div><div class="line">    Screenshot.getInstance ().init (driver);</div><div class="line">    prepare ();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>同样在在<code>AfterClass</code>后,进行了<code>driver</code>的退出.</p>
<p>正常情况下我们写测试用例是下面这种情况:</p>
<p>一个<code>TestClass</code>中包含多个<code>TestMethod</code>.如果每个<code>TestMethod</code>都相互独立,需要重新运行APP显然非常耗时,所以这里我将每一个<code>TestClass</code>相互独立,而其中的<code>TestMethod</code>又相互依赖,执行顺序通过<code>TestNG</code>的XML来控制.如下:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">test</span> <span class="attr">name</span>=<span class="string">"XX测试"</span>&gt;</span></div><div class="line">     <span class="tag">&lt;<span class="name">classes</span>&gt;</span></div><div class="line">         <span class="tag">&lt;<span class="name">class</span> <span class="attr">name</span>=<span class="string">"XXTest"</span>&gt;</span></div><div class="line">             <span class="tag">&lt;<span class="name">methods</span>&gt;</span></div><div class="line">                 <span class="tag">&lt;<span class="name">include</span> <span class="attr">name</span>=<span class="string">"testAAA"</span>/&gt;</span></div><div class="line">                 <span class="tag">&lt;<span class="name">include</span> <span class="attr">name</span>=<span class="string">"testBBB"</span>/&gt;</span></div><div class="line">                 <span class="tag">&lt;<span class="name">include</span> <span class="attr">name</span>=<span class="string">"testCCC"</span>/&gt;</span></div><div class="line">             <span class="tag">&lt;/<span class="name">methods</span>&gt;</span></div><div class="line">         <span class="tag">&lt;/<span class="name">class</span>&gt;</span></div><div class="line">     <span class="tag">&lt;/<span class="name">classes</span>&gt;</span></div><div class="line"> <span class="tag">&lt;/<span class="name">test</span>&gt;</span></div></pre></td></tr></table></figure>
<p>但是有些情况如果需要<code>TestMethod</code>也相互独立的话,可以利用<code>AfterMethod</code>注释.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@AfterMethod</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterMethod</span><span class="params">()</span> </span>&#123;</div><div class="line">    driver.resetApp ();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="编写用例"><a href="#编写用例" class="headerlink" title="编写用例"></a>编写用例</h1><h2 id="查找元素"><a href="#查找元素" class="headerlink" title="查找元素"></a>查找元素</h2><h3 id="定位方式"><a href="#定位方式" class="headerlink" title="定位方式"></a>定位方式</h3><p>查找元素可以通过很多方法(可以通过<code>UIAutomatorViewer</code>获取页面的id,name等信息):</p>
<ul>
<li>id</li>
<li>name</li>
<li>className </li>
<li>xpath</li>
<li>uiautomator</li>
</ul>
<h3 id="高级定位"><a href="#高级定位" class="headerlink" title="高级定位"></a>高级定位</h3><ul>
<li><p>用 <code>xpath</code>查找登录按钮: </p>
<pre><code>by.xpath (&quot;//button[@name=&apos;login&apos;]&quot;)
</code></pre></li>
</ul>
<p><a href="http://www.zvon.org/comp/r/tut-XPath_1.html#Pages~XPath_as_filesystem_addressing" target="_blank" rel="external">xpath实例教程</a></p>
<ul>
<li>用<code>uiautomator</code>的API滚动查找:</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">String rule = <span class="string">"new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().textContains(\""</span> + locator.value + <span class="string">"\"))"</span></div><div class="line">WebElement cl = driver.findElementByAndroidUIAutomator(rule));</div></pre></td></tr></table></figure>
<p> 其实就是用如果用uiautomator来写:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">new</span> UiScrollable(<span class="keyword">new</span> UiSelector().scrollable(<span class="keyword">true</span>)).scrollIntoView(<span class="keyword">new</span> UiSelector().textContains(value));</div></pre></td></tr></table></figure>
<p><a href="https://developer.android.com/reference/android/support/test/uiautomator/package-summary.html" target="_blank" rel="external">uiautomator API</a></p>
<pre><code>### 元素管理
</code></pre><p>为了让测试用例能够方便复用,简化写用例的时间,必要的封装是不可少的.<br>利用 <strong>Page Object</strong> 模式将页面上的元素进行封装.这样所有 Test 只需要简单的控制页面元素即可.</p>
<ul>
<li>可以使用yaml文件进行管理页面元素:</li>
</ul>
<p><img src="http://mio4kon.qiniudn.com/14869709464700.jpg?imageView2/1/w/900/h/440|imageMogr2" alt=""></p>
<p>然后在BasePage中将yaml解析封装成<code>Locator</code>对象.并保存到集合.<br>这样所有的PageObject都可以通过下面方法来定位元素.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">protected</span> Locator <span class="title">getLocator</span><span class="params">(String locatorName)</span> </span>&#123;</div><div class="line">        checkNotNull (locatorName);</div><div class="line">        Page page = getPage ();</div><div class="line">        List&lt;Locator&gt; locators = page.locators;</div><div class="line">        <span class="keyword">for</span>(Locator locator : locators) &#123;</div><div class="line">            <span class="keyword">if</span> (locatorName.equals (locator.name)) &#123;</div><div class="line">                <span class="keyword">return</span> locator;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">    &#125;</div><div class="line">    </div><div class="line"><span class="keyword">public</span> Locator 请输入手机号 = getLocator (<span class="string">"请输入手机号"</span>);</div><div class="line"><span class="keyword">public</span> Locator 请输入验证码 = getLocator (<span class="string">"请输入验证码"</span>);</div><div class="line"><span class="keyword">public</span> Locator 发送验证码 = getLocator (<span class="string">"发送验证码"</span>);</div></pre></td></tr></table></figure>
<p><del>TODO: 用工具生成对应page类</del><br>之所这么做是可以在用的时候直接通过IDE提示有哪些控件,但是写上述代码缺都是无聊的操作,所有我用了 <a href="http://freemarker.org/" target="_blank" rel="external">freemarker</a> 自动解析yaml来生成对应Page类,如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">Map&lt;String, Object&gt; input = <span class="keyword">new</span> HashMap&lt;String, Object&gt; ();</div><div class="line">   input.put (<span class="string">"pageName"</span>, page.pageName);</div><div class="line">   List&lt;LocatorObject&gt; genLocators = <span class="keyword">new</span> ArrayList&lt;&gt; ();</div><div class="line">   List&lt;Locator&gt; locators = page.locators;</div><div class="line">   <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; locators.size (); i++) &#123;</div><div class="line">       System.out.println (<span class="string">"生成Locator:"</span> + locators.get (i).name);</div><div class="line">       genLocators.add (<span class="keyword">new</span> LocatorObject (locators.get (i).name, locators.get (i).name));</div><div class="line">   &#125;</div><div class="line">   input.put (<span class="string">"locators"</span>, genLocators);</div><div class="line"></div><div class="line">   Template template = cfg.getTemplate (<span class="string">"page-temp.ftl"</span>);</div><div class="line"></div><div class="line">   Writer fileWriter = <span class="keyword">new</span> FileWriter (<span class="keyword">new</span> File (path, page.pageName + <span class="string">".java"</span>));</div><div class="line">   <span class="keyword">try</span> &#123;</div><div class="line">       template.process (input, fileWriter);</div><div class="line">   &#125; <span class="keyword">finally</span> &#123;</div><div class="line">       fileWriter.close ();</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>如果不会用 <code>freemarker</code>的可以搜索下相关资料.</p>
<h2 id="元素交互"><a href="#元素交互" class="headerlink" title="元素交互"></a>元素交互</h2><p>将交互事件,封装在<code>ElementActions</code>中.使用起来非常简单:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">actions.text (loginPage.请输入手机号, phone);</div><div class="line">actions.click (loginPage.发送验证码);</div><div class="line">actions.text (loginPage.请输入验证码, pwd);</div><div class="line">actions.click (loginPage.登录);</div></pre></td></tr></table></figure>
<p>常用的交互事件:单击,连击,上下左右滑动,后退,输入文本等等.</p>
<p>上面元素定位其实只是封装Locator,并没有真正的查找元素,查找元素实际上是在<code>ElementActions</code>中处理的.由于应用经常处理网络请求,控件有时候需要过段时间才可见,所以在查找控件时候需要给予一定的时间.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">WebElement element;</div><div class="line"><span class="keyword">try</span> &#123;</div><div class="line">    element = (<span class="keyword">new</span> WebDriverWait (mDriver, locator.timeOutInSeconds)).until (</div><div class="line">            <span class="keyword">new</span> ExpectedCondition&lt;WebElement&gt; () &#123;</div><div class="line"></div><div class="line">                <span class="meta">@Override</span></div><div class="line">                <span class="function"><span class="keyword">public</span> WebElement <span class="title">apply</span><span class="params">(WebDriver driver)</span> </span>&#123;</div><div class="line">                    List&lt;WebElement&gt; elements = getElement (locator);</div><div class="line">                    <span class="keyword">if</span> (elements.size () != <span class="number">0</span>) &#123;</div><div class="line">                        <span class="keyword">return</span> elements.get (<span class="number">0</span>);</div><div class="line">                    &#125;</div><div class="line">                    <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">                &#125;</div><div class="line">            &#125;);</div><div class="line"></div><div class="line">&#125; <span class="keyword">catch</span> (NoSuchElementException | TimeoutException e) &#123;</div><div class="line">    log.e (TAG, <span class="string">"超时[%4$d秒],找不到元素:[%1$s] , [By.%2$s : %3$s]"</span>, locator.name, locator.type, locator.value, locator.timeOutInSeconds);</div><div class="line">    <span class="keyword">throw</span> e;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="断言"><a href="#断言" class="headerlink" title="断言"></a>断言</h2><p>同样为了使用方便讲常用的断言封装,如Toast验证等.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">validatesToast</span><span class="params">(<span class="keyword">final</span> String msg)</span> </span>&#123;</div><div class="line">    checkNotNull (msg);</div><div class="line">    <span class="keyword">final</span> WebDriverWait wait = <span class="keyword">new</span> WebDriverWait (mDriver, <span class="number">10</span>);</div><div class="line">    assertNotNull (wait.until (ExpectedConditions</div><div class="line">                                       .presenceOfElementLocated (By.xpath (String.format (<span class="string">"//*[@text=\'%s\']"</span>, msg)))));</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="测试报告"><a href="#测试报告" class="headerlink" title="测试报告"></a>测试报告</h2><p>测试报告可以直观的展示测试的成功率,截图等信息.这里选择<a href="http://extentreports.com/" target="_blank" rel="external">extentreports</a>做为测试报告的框架.</p>
<p>最终效果:</p>
<p><img src="http://mio4kon.qiniudn.com/14869768987854.jpg?imageView2/1/w/900/h/440|imageMogr2" alt="-w1083"></p>
<h2 id="踩坑"><a href="#踩坑" class="headerlink" title="踩坑"></a>踩坑</h2><ul>
<li><code>findElementByName</code>无效.</li>
</ul>
<blockquote>
<p>Searching by name was deprecated over a year ago and removed from 1.5. In general, searching by accessibility id is better for a variety of reasons.</p>
</blockquote>
<p>如上<code>findElementByName</code>这个方法从<code>Appium 1.5</code>之后删除了,但是API不经能找到并且也没提示过时.这不坑爹嘛.后来使用下面的代码才解决用name查找元素的方法.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">String query = <span class="string">"new UiSelector().textContains"</span> + <span class="string">"(\""</span> + locator.value + <span class="string">"\")"</span>;</div><div class="line">webElements = mDriver.findElementsByAndroidUIAutomator (query);</div></pre></td></tr></table></figure>
<ul>
<li>据说<code>Appium 1.6.3</code>可以查找 Toast 的信息了.然后屁颠屁颠的跑去试了下网上的例子发现不好使啊.一度以为是Client版本的问题.搞了半天才发现需要加下面的代码:</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">capabilities.setCapability (MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);</div></pre></td></tr></table></figure>
<p>(感觉现在的Appium文档不全而且有点乱</p>
<ul>
<li>无意中发现测试的时候高德地图弹Toast报错.然后直接编译安装却不保存.猜测是不是在安装过程中Appium改了啥.看了下Service日志,竟然在安装的时候重新签名…</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">App not signed with debug cert.</div><div class="line">2017-02-13 18:17:19:848 - info: [debug] [ADB] Resigning apk.</div><div class="line">2017-02-13 18:17:23:938 - info: [debug] [ADB] Zip-aligning &apos;app-debug.apk&apos;</div><div class="line">2017-02-13 18:17:23:958 - info: [ADB] Checking whether zipalign is present</div><div class="line">2017-02-13 18:17:23:964 - info: [ADB] Using zipalign from /Users/mio4kon/Library/Android/sdk/build-tools/25.0.2/zipalign</div><div class="line">2017-02-13 18:17:23:968 - info: [debug] [ADB] Zip-aligning apk.</div><div class="line">2017-02-13 18:17:24:104 - info: [AndroidDriver] Remote apk path is /data/local/tmp/463eb03788048b4a1dacfe28545ee76e.apk</div></pre></td></tr></table></figure>
<p>解决方法:  </p>
<p><code>capabilities.setCapability (AndroidMobileCapabilityType.NO_SIGN, true);</code></p>


                <hr>

                

                <ul class="pager">
                    
                        <li class="previous">
                            <a href="/2017/02/20/利用 jEnv 管理 Mac OS 多java版本/" data-toggle="tooltip" data-placement="top" title="利用 jEnv 管理 Mac OS 多java版本">&larr; Previous Post</a>
                        </li>
                    
                    
                        <li class="next">
                            <a href="/2016/10/14/Atom之React Native配置/" data-toggle="tooltip" data-placement="top" title="React Native 开发工具之 Atom 配置">Next Post &rarr;</a>
                        </li>
                    
                </ul>

                

                
                <!-- disqus 评论框 start -->
                <div class="comment">
                    <div id="disqus_thread" class="disqus-thread"></div>
                </div>
                <!-- disqus 评论框 end -->
                

            </div>
    <!-- Side Catalog Container -->
        
            <div class="
                col-lg-2 col-lg-offset-0
                visible-lg-block
                sidebar-container
                catalog-container">
                <div class="side-catalog">
                    <hr class="hidden-sm hidden-xs">
                    <h5>
                        <a class="catalog-toggle" href="#">CATALOG</a>
                    </h5>
                    <ul class="catalog-body"></ul>
                </div>
            </div>
        

    <!-- Sidebar Container -->

            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                <!-- Featured Tags -->
                
                <section>
                    <!-- no hr -->
                    <h5><a href="/tags/">FEATURED TAGS</a></h5>
                    <div class="tags">
                       
                          <a class="tag" href="/tags/#Appium" title="Appium">Appium</a>
                        
                    </div>
                </section>
                

                <!-- Friends Blog -->
                
                <hr>
                <h5>FRIENDS</h5>
                <ul class="list-inline">

                    
                        <li><a href="http://lrd.ele.me/" target="_blank">lrd ele&#39;s Blog</a></li>
                    
                </ul>
                
            </div>

        </div>
    </div>
</article>




<!-- disqus 公共JS代码 start (一个网页只需插入一次) -->
<script type="text/javascript">
    /* * * CONFIGURATION VARIABLES * * */
    var disqus_shortname = "hexo-theme-huxblog";
    var disqus_identifier = "http://mio4kon.com/2017/02/13/自动化之旅--Appium/";
    var disqus_url = "http://mio4kon.com/2017/02/13/自动化之旅--Appium/";

    (function() {
        var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
        dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
    })();
</script>
<!-- disqus 公共JS代码 end -->




<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>
<!-- anchor-js, Doc:http://bryanbraun.github.io/anchorjs/ -->
<script>
    async("https://cdn.bootcss.com/anchor-js/1.1.1/anchor.min.js",function(){
        anchors.options = {
          visible: 'always',
          placement: 'right',
          icon: '#'
        };
        anchors.add().remove('.intro-header h1').remove('.subheading').remove('.sidebar-container h5');
    })
</script>
<style>
    /* place left on bigger screen */
    @media all and (min-width: 800px) {
        .anchorjs-link{
            position: absolute;
            left: -0.75em;
            font-size: 1.1em;
            margin-top : -0.1em;
        }
    }
</style>



    <!-- Footer -->
    <!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <ul class="list-inline text-center">
                
                
                

                
                    <li>
                        <a target="_blank" href="http://weibo.com/u/3198010790">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-weibo fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                

                

                
                    <li>
                        <a target="_blank"  href="https://github.com/Mio4kon">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                

                

                </ul>
                <p class="copyright text-muted">
                    Copyright &copy; Mio4kon 2019 
                    <br>
                    Theme by <a href="http://huangxuan.me">Hux</a> 
                    <span style="display: inline-block; margin: 0 5px;">
                        <i class="fa fa-heart"></i>
                    </span> 
                    Ported by <a href="http://blog.kaijun.rocks">Kaijun</a> | 
                    <iframe
                        style="margin-left: 2px; margin-bottom:-5px;"
                        frameborder="0" scrolling="0" width="91px" height="20px"
                        src="https://ghbtns.com/github-btn.html?user=kaijun&repo=hexo-theme-huxblog&type=star&count=true" >
                    </iframe>
                </p>
            </div>
        </div>
    </div>
</footer>

<!-- jQuery -->
<script src="/js/jquery.min.js"></script>

<!-- Bootstrap Core JavaScript -->
<script src="/js/bootstrap.min.js"></script>

<!-- Custom Theme JavaScript -->
<script src="/js/hux-blog.min.js"></script>


<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>

<!-- 
     Because of the native support for backtick-style fenced code blocks 
     right within the Markdown is landed in Github Pages, 
     From V1.6, There is no need for Highlight.js, 
     so Huxblog drops it officially.

     - https://github.com/blog/2100-github-pages-now-faster-and-simpler-with-jekyll-3-0  
     - https://help.github.com/articles/creating-and-highlighting-code-blocks/    
-->
<!--
    <script>
        async("http://cdn.bootcss.com/highlight.js/8.6/highlight.min.js", function(){
            hljs.initHighlightingOnLoad();
        })
    </script>
    <link href="http://cdn.bootcss.com/highlight.js/8.6/styles/github.min.css" rel="stylesheet">
-->


<!-- jquery.tagcloud.js -->
<script>
    // only load tagcloud.js in tag.html
    if($('#tag_cloud').length !== 0){
        async("http://mio4kon.com/js/jquery.tagcloud.js",function(){
            $.fn.tagcloud.defaults = {
                //size: {start: 1, end: 1, unit: 'em'},
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>

<!--fastClick.js -->
<script>
    async("https://cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
        var $nav = document.querySelector("nav");
        if($nav) FastClick.attach($nav);
    })
</script>


<!-- Google Analytics -->




<!-- Baidu Tongji -->


<!-- Side Catalog -->

<script type="text/javascript">
    function generateCatalog (selector) {
        var P = $('div.post-container'),a,n,t,l,i,c;
        a = P.find('h1,h2,h3,h4,h5,h6');
        a.each(function () {
            n = $(this).prop('tagName').toLowerCase();
            i = "#"+$(this).prop('id');
            t = $(this).text();
            c = $('<a href="'+i+'" rel="nofollow">'+t+'</a>');
            l = $('<li class="'+n+'_nav"></li>').append(c);
            $(selector).append(l);
        });
        return true;    
    }

    generateCatalog(".catalog-body");

    // toggle side catalog
    $(".catalog-toggle").click((function(e){
        e.preventDefault();
        $('.side-catalog').toggleClass("fold")
    }))

    /*
     * Doc: https://github.com/davist11/jQuery-One-Page-Nav
     * Fork by Hux to support padding
     */
    async("/js/jquery.nav.js", function () {
        $('.catalog-body').onePageNav({
            currentClass: "active",
            changeHash: !1,
            easing: "swing",
            filter: "",
            scrollSpeed: 700,
            scrollOffset: 0,
            scrollThreshold: .2,
            begin: null,
            end: null,
            scrollChange: null,
            padding: 80
        });
    });
</script>





<!-- Image to hack wechat -->
<img src="http://mio4kon.com/img/icon_wechat.png" width="0" height="0" />
<!-- Migrate from head to bottom, no longer block render and still work -->

</body>

</html>
